package com.nice.common.memory;

import java.nio.BufferOverflowException;
import java.nio.InvalidMarkException;

public class PooledBuffer {

    private int mark = -1;
    private int limit;
    private byte[] arr;
    private int capacity;
    private int readIndex;
    private int writeIndex;
    private BufferPool bufferPool;

    public PooledBuffer(byte[] arr, int startIndex, int capacity, BufferPool pooledBuffer) {
        this.arr = arr;
        this.capacity = capacity;
        this.writeIndex = startIndex;
        this.bufferPool = pooledBuffer;
    }

    public void put(byte b) {
        _put(writeIndex++, b);
        limit = writeIndex;
    }

    public void putChar(char c) {
        if (writeIndex >= capacity)
            throw new BufferOverflowException();
        BitUtil.putChar(this, writeIndex, c, true);
        writeIndex += 2;
        limit = writeIndex;
    }

    public void putShort(short c) {
        if (writeIndex >= capacity)
            throw new BufferOverflowException();
        BitUtil.putShort(this, writeIndex, c, true);
        writeIndex += 2;
        limit = writeIndex;
    }

    public void putInt(int c) {
        if (writeIndex >= capacity)
            throw new BufferOverflowException();
        BitUtil.putInt(this, writeIndex, c, true);
        writeIndex += 4;
        limit = writeIndex;
    }

    public void putLong(long c) {
        if (writeIndex >= capacity)
            throw new BufferOverflowException();
        BitUtil.putLong(this, writeIndex, c, true);
        writeIndex += 8;
        limit = writeIndex;
    }

    public void putFloat(float c) {
        if (writeIndex >= capacity)
            throw new BufferOverflowException();
        BitUtil.putFloat(this, writeIndex, c, true);
        writeIndex += 4;
        limit = writeIndex;
    }

    public void putDouble(double c) {
        if (writeIndex >= capacity)
            throw new BufferOverflowException();
        BitUtil.putDouble(this, writeIndex, c, true);
        writeIndex += 8;
        limit = writeIndex;
    }

    public void putArray(byte[] bs) {
        if (this.writeIndex + bs.length >= capacity)
            throw new BufferOverflowException();
        System.arraycopy(bs, 0, arr, writeIndex, bs.length);
        writeIndex += bs.length;
        limit = writeIndex;
    }

    public byte[] getArray(int size) {
        if (this.readIndex + size > limit)
            throw new BufferOverflowException();
        byte[] tmp = new byte[size];
        System.arraycopy(arr, readIndex, tmp, 0, size);
        readIndex += size;
        return tmp;
    }

    public byte get() {
        return _get(readIndex++);
    }

    public BufferPool getBufferPool() {
        return bufferPool;
    }

    public char getChar() {
        char c = BitUtil.getChar(this, readIndex, true);
        readIndex += 2;
        return c;
    }

    public short getShort() {
        short c = BitUtil.getShort(this, readIndex, true);
        readIndex += 2;
        return c;
    }

    public int getInt() {
        int c = BitUtil.getInt(this, readIndex, true);
        readIndex += 4;
        return c;
    }

    public long getLong() {
        long c = BitUtil.getLong(this, readIndex, true);
        readIndex += 8;
        return c;
    }

    public float getFloat() {
        float c = BitUtil.getFloat(this, readIndex, true);
        readIndex += 4;
        return c;
    }

    public double getDouble() {
        double c = BitUtil.getDouble(this, readIndex, true);
        readIndex += 8;
        return c;
    }

    public PooledBuffer mark() {
        mark = readIndex;
        return this;
    }

    public PooledBuffer reset() {
        int m = mark;
        if (m < 0)
            throw new InvalidMarkException();
        readIndex = m;
        return this;
    }

    public PooledBuffer clear() {
        mark = 0;
        limit = 0;
        readIndex = 0;
        writeIndex = 0;
        return this;
    }

    public int limit() {
        return limit;
    }

    public int getCapacity() {
        return capacity;
    }

    public PooledBuffer limit(int newLimit) {
        if ((newLimit > capacity) || (newLimit < 0))
            throw new IllegalArgumentException();
        limit = newLimit;
        if (readIndex > limit)
            readIndex = limit;
        if (mark > limit)
            mark = -1;
        return this;
    }

    public void release() {
//        if (bufferPool != null) {
//            bufferPool.recovery(this);
//        }
    }

    byte _get(int index) {
        if (index > limit)
            throw new BufferOverflowException();
        return arr[index];
    }

    void _put(int index, byte b) {
        if (index >= capacity)
            throw new BufferOverflowException();
        arr[index] = b;
    }
}
